﻿package net.jmp0.toast.entities.player 
{
	import net.jmp0.toast.entities.bases.GameElement;
	import net.jmp0.toast.entities.obstacles.Banana;
	import net.jmp0.toast.entities.obstacles.GameKey;
	import net.jmp0.toast.entities.obstacles.Goal;
	import net.jmp0.toast.entities.obstacles.Lock;
	import net.jmp0.toast.entities.obstacles.MagicOrange;
	import net.jmp0.toast.entities.obstacles.Pushable;
	import net.jmp0.toast.entities.obstacles.StandardWall;
	import net.jmp0.toast.entities.interfaces.wobblingBehavior.DoWobble;
	import net.jmp0.toast.entities.interfaces.wobblingBehavior.WobblingBehavior;
	import net.jmp0.toast.entities.obstacles.Teleporter;
	import net.jmp0.toast.entities.obstacles.Toaster;
	import net.jmp0.toast.levels.GameLevel;
	import net.jmp0.toast.levels.LevelTracker;
	import punk.core.Alarm;
	import punk.core.Entity;
	import punk.core.Spritemap;
	import punk.util.Input;
	import punk.core.World;
	import punk.util.Key;
	
	
	/**
	 * @version 0.0.1
	 * @author Thodd
	 */
	public class Player extends GameElement {
		
		// Ressources
		[Embed(source = '../../res/toast.png')] //Normal Sprite
			private var imgToast:Class;
			private var sprToast:Spritemap = FP.getSprite(imgToast, 20, 20);
			
		[Embed(source = '../../res/toast_burning.png')] //Burning Sprite, after M Toast is shot out of a Toaster
			private var imgToastBurning:Class;
			private var sprToastBurning:Spritemap = FP.getSprite(imgToastBurning, 20, 20);

		
		//Items the player can pick up
		public var keys:int = 0;
		public static var oranges:int = 0;
		
		
		/**
		 * Constructur
		 */
		public function Player(x:int, y:int) {
			super(x, y);
			
			this.sprite = sprToast;
			this.delay = (30 / 4);
			this.setHitbox(20, 20, 0, 0);
			this.type = "obstacle";
			
			//Key Mapping
			Input.define("right", Key.RIGHT, Key.D);
			Input.define("left", Key.LEFT, Key.A);
			Input.define("up", Key.UP, Key.W);
			Input.define("down", Key.DOWN, Key.S);
			Input.define("reset", Key.R);

		}
		
		
		/**
		 * Update
		 */
		override public function update():void {

			//The player can only perform an input if he isn't moving and there is no MessageBox instance opened
			if (!isMoving && ((FP.world as GameLevel).getMouseControl().currentMessageBox == null) &&
				(LevelTracker.currentOverlay == null)) {
				checkForInput();
			}
			
			if (xSpeed != 0 || ySpeed != 0) {
				performMovement();
			}
			
			if (isBurning) {
				this.sprite = sprToastBurning;
			}else {
				this.sprite = sprToast;
			}
			
			checkIfOutsideLevel();
			
			checkIfResetWasPressed();
			
		}
		
		
		/**
		 * Checks if the player has left the level, if that's the case the level is restarted
		 */
		private function checkIfOutsideLevel():void {
			if (x < 0 || x > FP.screen.width || y < 0 || y > FP.screen.height) {
				(FP.world as GameLevel).restartLevel();
			}
		}
		
		
		/**
		 * Reset the level if the player has pressed "reset"
		 */
		private function checkIfResetWasPressed():void {
			if (Input.pressed("reset")) {
				(FP.world as GameLevel).restartLevel();
			}
		}
		
		
		/**
		 *  Checks for Input and sets the xSpeed/ySpeed
		 */
		private function checkForInput():void {
			if (Input.pressed("right")) {
				xSpeed = movingSpeed;
			}else if (Input.pressed("left")) {
				xSpeed = -1 * movingSpeed;
			}else if (Input.pressed("up")) {
				ySpeed = -1 * movingSpeed;
			}else if (Input.pressed("down")) {
				ySpeed = movingSpeed;
			}
			
			//If xSpeed or ySpeed was set to a value > 0, the player moves on to the
			//"isMoving" State.
			if (xSpeed != 0 || ySpeed != 0) {
				isMoving = true;
			}
			
		}
		
		
		/**
		 * Performs the Movement and Checks for Collisions before actually moving
		 * the object.
		 */
		private function performMovement():void {
			
			//Determining the distance the Object should move, either it's xSpeed or ySpeed
			//depending which of those variables is != 0
			var travelingDistance:int = (xSpeed != 0) ? xSpeed : ySpeed;
			
			//Move the Player on the x-Axis with xSpeed
			for (var i:int = 0; i < Math.abs(travelingDistance); i++) {
				//Check for a collision
				var collisionEntity:Entity = this.collide("obstacle", x + FP.sign(xSpeed), y + FP.sign(ySpeed));

				//If there is none, then move the player
				// "lastCollision" is used to prevent double collision performing
				if (collisionEntity == null || collisionEntity == lastCollision) {
					x += FP.sign(xSpeed);
					y += FP.sign(ySpeed);
					
				}else if (collisionEntity != null) {
					//reset the last collision
					lastCollision = null;
					//if there was a collision, downcast the entity to a GameElement instance
					//Actually since we checked for a collision with type="obstacle" collisionEntity is always a GameElement
					if (collisionEntity is GameElement) {
						var ge:GameElement = collisionEntity as GameElement;
						performCollisions(ge);
					}
					
				}
				
			}
			
		}
		
		
		/**
		 * Find out with which object the player collided and invoke some collision behavior
		 * @param	collisionEntity : the collision partner of type GameElement
		 */
		private function performCollisions(collisionEntity:GameElement):void {

			collisionEntity.getWobblingBehavior().wobble(this);
			
			//stop the movement if there was a collision
			//doesn't matter which obstacle we hit
			oldXspeed = xSpeed;
			oldYspeed = ySpeed;
			xSpeed = 0;
			ySpeed = 0;
			isMoving = false;
			
			/**
			 * Specific collision behavior starts here
			 */
			if (collisionEntity is StandardWall) {
				if (isBurning) {
					collisionEntity.destroy();
					isBurning = false;
				}
				
			}else if (collisionEntity is Banana) {
				performCollisionWithBanana((collisionEntity as Banana));
				
			}else if (collisionEntity is Toaster) {
				performCollisionWithToaster((collisionEntity as Toaster));
				
			}else if (collisionEntity is Teleporter) {
				performCollisionWithTeleporter((collisionEntity as Teleporter));
				
			}else if (collisionEntity is GameKey) {
				var k:GameKey = (collisionEntity as GameKey);
				k.performCollision(this);
				this.keys += k.getAmount();
				collisionEntity.destroy();
				
			}else if (collisionEntity is Lock) {
				if (this.keys >= 1) {
					collisionEntity.destroy();
					this.keys -= 1;
					this.xSpeed = oldXspeed;
					this.ySpeed = oldYspeed;
				}else {
					this.isBurning = false;
				}
				
			}else if (collisionEntity is Pushable) {
				collisionEntity.xSpeed = this.oldXspeed;
				collisionEntity.ySpeed = this.oldYspeed;
				this.isBurning = false;
				
			}else if (collisionEntity is MagicOrange) {
				var mo:MagicOrange = (collisionEntity as MagicOrange);
				mo.performCollision(this);
				oranges += mo.getAmount();
				collisionEntity.destroy();
				
			}else if (collisionEntity is Goal) {
				collisionEntity.performCollision(this);
			}
 
		}
		
		
		/**
		 * Performs a collision event with a Teleporter... man this is obvious, isn't it?
		 * @param	colTeleporter : yadda yadda the teleporter you collided with
		 */
		public function performCollisionWithTeleporter(colTeleporter:Teleporter):void {
			colTeleporter.performCollision(this);
		}
		
		
		/**
		 * Implementation of the special collision behavior of a Toaster
		 * @param	colToaster : you get it
		 */
		private function performCollisionWithToaster(colToaster:Toaster):void {
			
			colToaster.performCollision(this);
			
			//If the player was correctly shot out of the toaster -> he is burning
			if (this.xSpeed != 0 || this.ySpeed != 0) {
				this.isBurning = true;
				this.isMoving = true;
			}
			
		}
		
		
		/**
		 * Implementation of the special collision behavior of a banana
		 * Yeah i know, this code sucks but im too lazy to refactor it
		 * @param	colBanana : you get it
		 */
		private function performCollisionWithBanana(colBanana:Banana):void {
			
			colBanana.performCollision(this);
			
			//If the player was correctly redirected, he is still moving
			//this doesn't affect the burning state
			if (this.xSpeed != 0 || this.ySpeed != 0) {
				this.isMoving = true;
			}else {
				//if the player is not moving, then he was stopped -> he also stops burning
				this.isBurning = false;
			}
			
		}
		
		
		/**
		 * Setter and Getters, i know this app is poorly designed...
		 * some times i use getters, sometimes i don't.
		 * You know that groovy and grails automatically generate this stuff at runtime, why not AS? ;(
		 */
		public function getKeys():int {
			return keys;
		}
		
		public function getOranges():int {
			return oranges;
		}
		
	}

}